package com.lagou.edu.ioc;


import com.lagou.edu.aop.ITransactionManager;
import com.lagou.edu.aop.Transactional;
import com.lagou.edu.aop.TransactionalInvocationHandler;
import com.lagou.edu.aop.TransactionalProxyFactory;
import com.lagou.edu.utils.ClassUtil;
import com.lagou.edu.utils.ConnectionUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 描述:
 *  1. 读取xml，获取到扫描包路径
 *  2. 获取到每个类上的注解，将标记为@Service的类加入到ioc容器中
 *  3. 获取到为@Autowired标记的属性，将属性值赋值为ioc容器中的对象，完成依赖注入
 *  4. 完成aop动态代理，完成事务的 控制
 */
public class BeanFactory {

    public static void main(String[] args) {
        Class<BeanFactory> beanFactoryClass = BeanFactory.class;
    }

    public static Map<String,Object> iocContainer = new HashMap<>();


    public static void execute(){
        InputStream inputStream = readBeansXmlConfig("beans.xml");
        try {
            parseBeansXmlConfig(inputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(iocContainer);
    }

    /**
     * 1. 读取xml
     */
    private static InputStream readBeansXmlConfig(String xmlConfigName) {
        return BeanFactory.class.getClassLoader().getResourceAsStream(xmlConfigName);
    }

    /**
     * 获取到扫描包路径
     * @param is
     * @throws DocumentException
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private static void parseBeansXmlConfig(InputStream is) throws Exception {
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(is);
        Element rootElement = document.getRootElement();
        Element componentScan = (Element) rootElement.selectSingleNode("//componentScan");
        String packageClasspath = componentScan.attributeValue("package");
        // 获取到某路径下的所有类
        Set<Class<?>> classSet = ClassUtil.getClassSet(packageClasspath);
        // 如果类上存在@Service 或者 @Controller 注解，则加入ioc容器中
        addIOC(classSet);
        // 依赖注入 且处理声明式事务
        doDI();
    }

    private static void addIOC(Set<Class<?>> classSet) throws InstantiationException, IllegalAccessException {
        for (Class<?> aClass : classSet) {
            // @Service处理
            Service serviceAnnotation = aClass.getDeclaredAnnotation(Service.class);
            if (serviceAnnotation != null) {
                Object o = aClass.newInstance();
                String id = serviceAnnotation.value();
                if (getValue(id) == null) {
                    iocContainer.put(id,o);
                }
            }
            // @Controller处理
            Component controllerAnnotation = aClass.getDeclaredAnnotation(Component.class);
            if (controllerAnnotation != null) {
                Object o = aClass.newInstance();
                String id1 = controllerAnnotation.value();
                if (getValue(id1) == null) {
                    iocContainer.put(id1,o);
                }
            }
        }
    }

    private static void doDI() throws Exception {

        ITransactionManager transactionManager = (ITransactionManager) getValue("transactionManager");
        if (transactionManager == null) {
            throw new Exception("您的事务类暂未加入到ioc容器中！");
        }

        for (Map.Entry<String, Object> entry : iocContainer.entrySet()) {
            Object obj = entry.getValue();
            Class<?> aClass = obj.getClass();

            // 声明式事务处理
            TransactionalProxyFactory transactionalProxyFactory = new TransactionalProxyFactory(new TransactionalInvocationHandler(transactionManager,obj));
            Object transactionalProxyObject = transactionalProxyFactory.getTransactionalProxyObject(obj);
            Method[] declaredMethods = aClass.getDeclaredMethods();
            for (Method declaredMethod : declaredMethods) {
                Transactional annotation = declaredMethod.getAnnotation(Transactional.class);
                if (annotation != null) {
                    iocContainer.put(entry.getKey(), transactionalProxyObject);
                }
            }

            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                // @Autowired注解处理
                Autowired autowiredAnnotation = declaredField.getAnnotation(Autowired.class);
                if (autowiredAnnotation != null) {
                    String id = autowiredAnnotation.value();
                    Object value = getValue(id);
                    if(value == null) {
                        throw new Exception("ioc中不存在" + value + "对象！");
                    }
                    declaredField.setAccessible(true);
                    declaredField.set(obj,value);
                }
            }
        }
    }


    public static Object getValue(String key) {
        return iocContainer.get(key);
    }

}
